package com.hyt.it.ogt.opertaion.track.util;

import com.hyt.core.util.StringUtils;
import com.hyt.it.ogt.opertaion.track.annotations.FieldImportVal;
import com.hyt.it.ogt.opertaion.track.annotations.FieldVal;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;

import java.beans.IntrospectionException;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;

/**
 * @author 朱振宇
 * @since 2022-11-23
 */
public class AspectUtils {

    public static List<Map<String, Object>> getFieldValMap(Object[] args, JoinPoint joinPoint, Annotation annotation) {
        List<Map<String, Object>> res = new ArrayList<>();
        String[] parameterNames = null;
        if(Objects.nonNull(joinPoint)) {
            parameterNames = ((MethodSignature) joinPoint.getSignature()).getParameterNames();
        }
        Method[] methods = annotation.annotationType().getMethods();
        Map<String, Object> fieldValMap = findFieldMap(methods, annotation);
        Map<String, Object> fieldImportMap = findFieldImportMap(methods, annotation);
        Map<String, Object> fieldMap = fieldValMap.entrySet().stream().filter(m->!StringUtils.isEmpty(m.getKey())).collect(Collectors.toMap(m->m.getKey(), m->m.getValue()));
        for(int i=0;i<args.length;i++) {
            final List<Map<String, Object>>[] mapList = new List[]{new ArrayList<>()};
            if(args[i] instanceof String || args[i] instanceof Number) {
                if(Objects.nonNull(parameterNames)) {
                    if(!(mapList[0] = buildMap(fieldMap, parameterNames[i], args[i])).isEmpty()) {
                        res.addAll(mapList[0]);
                    }
                }
            } else if(args[i] instanceof Collection) {
                final int j = i;
                String[] finalParameterNames = parameterNames;
                final List<Map<String, Object>>[] tempList = new List[]{mapList[0]};
                ((Collection) args[j]).stream().forEach(arg->{
                    if(arg instanceof String || arg instanceof Number) {
                        if(Objects.nonNull(finalParameterNames)) {
                            if(!(tempList[0] = buildMap(fieldMap, finalParameterNames[j], arg)).isEmpty()) {
                                res.addAll(tempList[0]);
                            }
                        }
                    }else if(arg instanceof Map){
                        if(!(mapList[0] = buildMap(fieldMap, arg)).isEmpty()) {
                            res.addAll(mapList[0]);
                        }
                    }else {
                        if(!(mapList[0] = buildMap(fieldMap, arg)).isEmpty()) {
                            res.addAll(mapList[0]);
                        }
                    }
                });
            } else if(args[i] instanceof Map) {
                if(!(mapList[0] = buildMap(fieldMap, args[i])).isEmpty()) {
                    res.addAll(mapList[0]);
                }
            } else {
                if(!(mapList[0] = buildMap(fieldMap, args[i])).isEmpty()) {
                    res.addAll(mapList[0]);
                }
            }
        }
        setImportField(args, fieldImportMap, res);
        return res;
    }

    private static void setImportField(Object[] args, Map<String, Object> fieldImportMap, List<Map<String, Object>> resList) {
        if(resList.isEmpty()) {
            return;
        }
        for(int i=0;i<args.length;i++) {
            buildImportMap(fieldImportMap, args[i]);
        }
        AtomicBoolean setImportFieldByOuterField = new AtomicBoolean(false);
        resList.stream().forEach(vo-> {
                fieldImportMap.entrySet().forEach(m->{
                    if(!vo.containsKey(m.getKey())) {
                        vo.put(m.getKey(), m.getValue());
                        setImportFieldByOuterField.set(true);
                    }
                });
            }
        );
        if(setImportFieldByOuterField.get()) {
            resList.remove(resList.size() - 1);
        }
    }

    private static Map<String, Object> findFieldMap(Method[] methods, Annotation annotation) {
        return Arrays.stream(methods).filter(f-> f.isAnnotationPresent(FieldVal.class)).collect(
                        Collectors.toMap(f-> {
                            try {
                                return f.invoke(annotation).toString();
                            } catch (IllegalAccessException | InvocationTargetException e) {
                                e.printStackTrace();
                                throw new RuntimeException(annotation.annotationType() + "查询注解属性值异常");
                            }
                        }, f -> new Object())
                );
    }

    private static Map<String, Object> findFieldImportMap(Method[] methods, Annotation annotation) {
        return Arrays.stream(methods).filter(f->f.isAnnotationPresent(FieldImportVal.class)).collect(
                Collectors.toMap(f-> {
                    try {
                        return f.invoke(annotation).toString();
                    } catch (IllegalAccessException | InvocationTargetException e) {
                        e.printStackTrace();
                        throw new RuntimeException(annotation.annotationType() + "查询注解属性值异常");
                    }
                }, f -> new Object())
        );
    }

    private static void buildImportMap(Map<String, Object> importMap, Object arg) {
        try {
            Map<String, Object> argMap;
            if(arg instanceof Collection) {
                Collection collection = (Collection) arg;
                collection.stream().forEach(obj->buildImportMap(importMap, obj));
            }else {
                argMap = BeanUtils.convertBeanToMap(arg);
                argMap.entrySet().stream().forEach(map->{
                    if(map.getValue() instanceof Collection) {
                        buildImportMap(importMap, map.getValue());
                    }else {
                        if(importMap.containsKey(map.getKey()) && BeanUtils.isEmpty(importMap.get(map.getKey()))) {
                            importMap.put(map.getKey(), map.getValue());
                        }
                    }
                });
            }
        } catch (IntrospectionException | InvocationTargetException | IllegalAccessException e) {
            e.printStackTrace();
            throw new RuntimeException("获取注解@FieldImportField异常");
        }
    }

    private static List<Map<String, Object>> buildMap(Map<String, Object> fieldValMap, Object arg) {
        List<Map<String, Object>> resList = new ArrayList<>();
        try {
            Map<String, Object> argMap;
            if(arg instanceof Collection) {
                Collection collection = (Collection) arg;
                collection.stream().forEach(obj->resList.addAll(buildMap(fieldValMap, obj)));
            }else {
                argMap = BeanUtils.convertBeanToMap(arg);
                AtomicBoolean m = new AtomicBoolean(false);
                argMap.entrySet().stream().forEach(map->{
                    if(map.getValue() == null) {
                        return;
                    }
                    if(map.getValue() instanceof Collection || BeanUtils.isClass(map.getValue())) {
                        resList.addAll(buildMap(fieldValMap, map.getValue()));
                    } else {
                        if(fieldValMap.containsKey(map.getKey())) {
                            m.set(true);
                        }
                    }
                });
                if(m.get()) {
                    resList.add(argMap);
                }
            }
        } catch (IntrospectionException | InvocationTargetException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return resList;
    }

    private static List<Map<String, Object>> buildMap(Map<String, Object> fieldValMap, String parameterName, Object arg) {
        List<Map<String, Object>> resList = new ArrayList<>();
        if(fieldValMap.containsKey(parameterName)) {
            resList.add(new HashMap(){
                {
                    put(parameterName, arg);
                }
            });
        }
        return resList;
    }
}
